use core::arch::asm;

use crate::sbi;
use crate::sync::UPSafeCell;
use crate::trap::TrapContext;
use lazy_static::*;

const USER_STACK_SIZE: usize = 4096 * 2;
const KERNEL_STACK_SIZE: usize = 4096 * 2;
const MAX_APP_NUM: usize = 16; //最多支持应用程序
const APP_SIZE_LIMIT: usize = 0x20000; //应用程序镜像大小限制
const APP_BASE_ADDRESS: usize = 0x80400000; //约定的应用程序内存地址

//内核数据段.bss 中初始化一个内核栈?
static KERNEL_STACK: KernelStack = KernelStack {
    data: [0; KERNEL_STACK_SIZE],
};
static USER_STACK: UserStack = UserStack {
    data: [0; USER_STACK_SIZE],
};

#[repr(align(4096))] //强制4kb对齐
struct KernelStack {
    data: [u8; KERNEL_STACK_SIZE],
}

impl KernelStack {
    ///获取内核栈初始栈顶
    fn get_sp(&self) -> usize {
        self.data.as_ptr() as usize + KERNEL_STACK_SIZE
    }

    pub fn push_context(&self, cx: TrapContext) -> usize {
        let new_cx_ptr = self.get_sp() - core::mem::size_of::<TrapContext>();
        unsafe {
            *(new_cx_ptr as *mut TrapContext) = cx; //cx的值复制到内核栈中指定位置
        }
        new_cx_ptr
    }
}

#[repr(align(4096))]
struct UserStack {
    data: [u8; USER_STACK_SIZE],
}

impl UserStack {
    ///获取用户栈初始栈顶
    fn get_sp(&self) -> usize {
        self.data.as_ptr() as usize + USER_STACK_SIZE
    }
}

//管理应用程序的全局数据结构
lazy_static! {
    static ref APP_MANAGER: UPSafeCell<AppManager> = unsafe {
        //获取编译后_num_app符号地址，link_app.S中_num_app处定义了一个数组，其中第一个元素是数组长度，
        unsafe extern "C" {
            safe fn _num_app();
        }
        //内存地址强转裸指针
        let num_app_ptr = _num_app as usize as *const usize;
        //读取数组第一个元素，即数组长度
        let app_num = num_app_ptr.read_volatile();
        //根据裸指针读取数组切片
        let app_start_raw: &[usize] = core::slice::from_raw_parts(num_app_ptr.add(1), app_num+1);
        //创建数组，并复制元素
        let mut apps_start: [usize;MAX_APP_NUM+1] = [0; MAX_APP_NUM+1];
        apps_start[..=app_num].copy_from_slice(app_start_raw);
        UPSafeCell::new(AppManager::new(app_num,0,apps_start))
    };
}

struct AppManager {
    app_num: usize,                       //应用程序数量
    current_app: usize,                   //当前运行应用程序的索引
    apps_start: [usize; MAX_APP_NUM + 1], //应用程序起始地址
}
impl AppManager {
    fn new(app_num: usize, current_app: usize, apps_start: [usize; MAX_APP_NUM + 1]) -> Self {
        Self {
            app_num,
            current_app,
            apps_start,
        }
    }

    ///核心功能是将内存中内核数据段data中的应用代码复制到约定的内存地址 0x80400000
    fn load_app(&self, app_id: usize) {
        if app_id >= self.app_num {
            println!("All applications completed!");
            sbi::shutdown(false);
        }
        println!("[kernel] Loading app_{}", app_id);
        unsafe {
            //先清空约定的内存区域0x80400000
            core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, APP_SIZE_LIMIT).fill(0);
            //计算应用程序大小
            let app_start_ptr = self.apps_start[app_id] as *const u8;
            let app_len = self.apps_start[app_id + 1] - self.apps_start[app_id];
            //内存copy
            let app_src = core::slice::from_raw_parts(app_start_ptr, app_len);
            let app_dst =
                core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, app_src.len());
            app_dst.copy_from_slice(app_src);
            // memory fence about fetching the instruction memory
            asm!("fence.i");
        }
    }

    pub fn get_current_app(&self) -> usize {
        self.current_app
    }

    pub fn move_to_next_app(&mut self) {
        self.current_app += 1;
    }
}

/// run next app
///此函数一定在内核态被调用，核心逻辑：
/// 1. 初始化一个TrapContext结构体，设置将要运行应用程序的入口地址、用户栈地址、SCR等信息
/// 2. 将TrapContext放入内核栈中（初次运行内核栈为空、或覆盖上一个已结束应用程序的TrapContext）
/// 3. 从系统调用中恢复（执行trap.S中的__restore）
pub fn run_next_app() -> ! {
    let mut app_manager = APP_MANAGER.exclusive_access();
    let app_id = app_manager.get_current_app();
    app_manager.move_to_next_app();
    app_manager.load_app(app_id);
    //释放app_manager引用
    drop(app_manager);
    //声明这是一个C语言ABI的外部函数调用
    unsafe extern "C" {
        unsafe fn __restore(cx_addr: usize);
    }
    let cx = TrapContext::app_init_context(APP_BASE_ADDRESS, USER_STACK.get_sp()); //USER_STACK.get_sp()相当于用户栈也初始化了
    let new_cx_ptr = KERNEL_STACK.push_context(cx);
    unsafe {
        __restore(new_cx_ptr);
    }
    panic!("Unreachable in batch::run_current_app!");
}
